﻿#region Using namespaces

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;

#endregion

namespace BOL.Apps.GeneNetworkBrowser.ViewModels
{
    public abstract class ViewModelBase : IDisposable, INotifyPropertyChanged, IChangeTracking
    {
        #region Private variables

        protected string _title;

        #endregion

        #region Public properties

        public virtual string Title
        {
            get { return _title; }
            set
            {
                if (_title != value)
                {
                    _title = value;
                    IsChanged = true;
                    OnPropertyChanged("Title");
                }
            }
        }
        
        #endregion
        
        #region Constructor

        protected ViewModelBase()
        {
            PropertyChanged += new PropertyChangedEventHandler(OnNotifiedOfPropertyChanged);
        }

        #endregion

        #region IDisposable implementation

        public void Dispose()
        {
            OnDispose();
        }

        protected virtual void OnDispose() { }

        #endregion

        #region IChangeTracking implementation

        private bool _notifyingObjectIsChanged;
        private readonly object _notifyingObjectIsChangedSyncRoot = new Object();
        public event EventHandler<EventArgs> Changed;

        /// <summary> 
        /// Gets the object's changed status. 
        /// </summary> 
        /// <value> 
        /// <see langword="true"/> if the object’s content has changed since the last call to <see cref="AcceptChanges()"/>; otherwise, <see langword="false"/>.  
        /// The initial value is <see langword="false"/>. 
        /// </value> 
        public bool IsChanged
        {
            get
            {
                lock (_notifyingObjectIsChangedSyncRoot)
                {
                    return _notifyingObjectIsChanged;
                }
            }
            set
            {
                lock (_notifyingObjectIsChangedSyncRoot)
                {
                    if (!Boolean.Equals(_notifyingObjectIsChanged, value))
                    {
                        _notifyingObjectIsChanged = value;

                        OnPropertyChanged("IsChanged");

                        if (Changed != null)
                            Changed(this, EventArgs.Empty);
                    }
                }
            }
        } 

        /// <summary> 
        /// Resets the object’s state to unchanged by accepting the modifications. 
        /// </summary> 
        public void AcceptChanges()
        {
            IsChanged = false;
        }

        /// <summary> 
        /// Handles the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for this object. 
        /// </summary> 
        /// <param name="sender">The source of the event.</param> 
        /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that contains the event data.</param> 
        private void OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal))
                IsChanged = true;
        } 

        #endregion

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, e);
        } 

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        protected void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                throw new ArgumentNullException("PropertyNames cannot be null.");

            foreach (var propertyName in propertyNames)
                OnPropertyChanged(propertyName);
        } 

        #endregion
    }
}
